package com.faxsun.web.security.authentication;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springside.modules.utils.PropertiesLoader;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.faxsun.core.persist.entity.UserPO;
import com.faxsun.core.pojos.enums.AccountType;
import com.faxsun.core.service.UserService;
import com.faxsun.web.utils.SessionUtils;

import mblog.core.pojos.User;
import mtons.modules.pojos.UserProfile;

public class WeiXin extends ThirdPartyLoginAbs {
	private static final Logger log = LoggerFactory.getLogger(WeiXin.class);
	protected static PropertiesLoader propertiesLoader = new PropertiesLoader("classpath:/wechatconfig.properties");

	private static final String APPID = "?appid=" + propertiesLoader.getProperty("AppID");
	private static final String REDIRECT = "&redirect_uri=";
	private static final String URL = propertiesLoader.getProperty("redirect_uri");
	private static final String RESP = "&response_type=code";
	private static final String SCOPE = "&scope=" + propertiesLoader.getProperty("scope");
	private static final String APPENDIX = "#wechat_redirect";
	private static final String GRANT = "&grant_type=authorization_code";
	private static final String SECRET = "&secret=" + propertiesLoader.getProperty("AppSecret");
	private static final String CODE = "&code=";
	private static final String INVALID_CODE = "40029";
	private static final String ACCESS_TOKEN = "?access_token=";
	private static final String OPEN_ID = "&openid=";

	@Autowired
	ThirdPartyLoginServer loginServer;

	@Autowired
	UserService userService;

	// 重定向URL，微信返回带code参数的REDIRECT_URI
	@Override
	public void ResponseURL(HttpServletRequest request, HttpServletResponse response) {
		// https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI
		// &response_type=code&scope=SCOPE&state=STATE#wechat_redirect
		String ENCODING = "utf-8";
		String REDIRECT_URI = null;
		try {
			REDIRECT_URI = REDIRECT + URLEncoder.encode(URL, ENCODING);
		} catch (UnsupportedEncodingException e1) {
			log.error(" URLEncoder WeiXin config redirect_URL error ", e1);
			e1.printStackTrace();
		}

		String url = propertiesLoader.getProperty("baseURL") + APPID + REDIRECT_URI + RESP + SCOPE + APPENDIX;
		try {
			response.sendRedirect(url);
		} catch (IOException e) {
			log.error("WeiXin response sendRedirect error ", e);
			e.printStackTrace();
		}

	}

	// 解析出code [and state (state 用于保持请求和回调的状态，授权请求后原样带回给第三方，该参数可用于防止csrf攻击) 本函数未
	// 采用]
	@Override
	public ThirdPartyObj get3rdObj(HttpServletRequest request, HttpServletResponse response, String channel,
			String path) {

		ThirdPartyObj obj3rd = new ThirdPartyObj();
		obj3rd.setChannelId(channel);

		String code = request.getParameter("code");
		if (code == null) {
			log.info("微信未授权");
			return obj3rd;
		}
		obj3rd.setCode(code);

		// get access token by HttpClient
		// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
		String url = propertiesLoader.getProperty("accessTokenURL") + APPID + SECRET + CODE + code + GRANT;
		String respJson = getHttpResponse(url);
		JSONObject jo = JSON.parseObject(respJson);
		String accessToken = jo.getString("access_token");
		if (accessToken == null || accessToken.equals(INVALID_CODE)) {
			// 我们的网站被CSRF攻击了或者用户取消了授权
			log.info("授权所需code无效");
		} else {

			obj3rd.setAccessToken(accessToken);
			obj3rd.setUid(jo.getString("openid"));

			// get nickname
			// GET userInfo:
			// https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
			String userinfo_url = propertiesLoader.getProperty("userInfoURL") + ACCESS_TOKEN + obj3rd.getAccessToken()
					+ OPEN_ID + obj3rd.getUid();
			String resp_json = getHttpResponse(userinfo_url);

			jo = JSON.parseObject(resp_json);
			obj3rd.setUnionId(jo.getString("unionid"));
			obj3rd.setAvatar(jo.getString("headimgurl"));
			obj3rd.setNickName(jo.getString("nickname"));
		}

		return obj3rd;
	}

	@Override
	public String login(HttpSession session, ThirdPartyObj obj) {
		if (obj.getUid() == null || obj.getUid().isEmpty()) {
			return HOME; // Auth Login Failed! Inform, HOME---> FAIL Page
		}
		UserProfile up = userService.findByThirdId(obj.getUnionId(), AccountType.WECHAT);
		if (up == null) {
			// TODO: 本地无用户，注册用户
			UserPO userPO = transformFromWechat(obj);
			up = this.userService.createLocalUser(userPO);
		} else {
			// TODO: 规范置Session逻辑
			User userPO = this.userService.get(up.getId());
			userPO.setLastLogin(new Date());
			// 更新头像和昵称
			userPO.setAvatar(obj.getAvatar());
			userPO.setName(obj.getNickName());
			up=this.userService.update(userPO);
		}
		// 登录成功，设置Session，返回跳转链接
		SessionUtils.putProfile(session, up, true);
		return HOME;
	}

	/**
	 * 将{@link ThirdPartyObj} 转成{@link UserPO}
	 * 
	 * @param obj
	 * @return
	 */
	private UserPO transformFromWechat(ThirdPartyObj obj) {
		UserPO userPO = new UserPO();
		userPO.setAvatar(obj.getAvatar());
		userPO.setUsername(obj.getUnionId());
		userPO.setName(obj.getNickName());
		userPO.setThirdLogin(obj.getUid());
		userPO.setIsLocal(1);
		userPO.setCreated(new Date());
		userPO.setRoleLevel(0);
		userPO.setWechatUnionId(obj.getUnionId());
		userPO.setLastLogin(new Date());

		return userPO;
	}

	private String getHttpResponse(String resUrl) {
		// 创建HttpClient实例
		CloseableHttpClient httpClient = HttpClients.createDefault();
		// HTTP Get请求
		HttpGet httpgets = new HttpGet(resUrl);
		CloseableHttpResponse httpResponse = null;
		// 设置请求和传输超时时间
		Integer timeOut = 2000; // 2 秒 超时
		RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeOut)
				.setConnectTimeout(timeOut).setSocketTimeout(timeOut).build();
		httpgets.setConfig(requestConfig);
		HttpEntity entity = null;
		try {
			httpResponse = httpClient.execute(httpgets);
			entity = httpResponse.getEntity();
			String response = EntityUtils.toString(entity, "UTF-8");
			return response;
		} catch (ClientProtocolException e) {
			log.error("http protocol error：" + e.getMessage());
		} catch (IOException e) {
			log.error("http IO error：" + e.getMessage());
		} catch (Exception e) {
			log.error("httpException：" + e.getMessage());
		} finally {
			try {
				httpResponse.close();
			} catch (IOException e) {
				log.error("http IO error ：" + e.getMessage());
			} catch (NullPointerException e) {
				log.error("http NullPointer error：" + e.getMessage());
			}
		}
		return null;
	}

}
